package com.gavin.lingxi.rtclib;

import android.content.Context;
import android.util.Log;


import com.gavin.lingxi.rtclib.util.ThreadUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mym.plog.PLog;
import org.webrtc.DataChannel;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SessionDescription;
import org.webrtc.StatsReport;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoSink;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Set;


public class LiveRtcService implements PeerConnectionClient.PeerConnectionEvents {

    private static final String TAG = LiveRtcService.class.getSimpleName();

    private Context mContext;

    private AppRTCAudioManager audioManager = null;
    private PeerConnectionClient peerConnectionClient = null;
    private PeerConnectionParameters peerConnectionParameters;


    private Options mOptions;
    private long callStartedTimeMs, callEndTimeMs;
    private boolean isStarted = false;
    private LiveRtcServiceListener mLiveRtcServiceListener;
    private String uid, did;


    private AppRTCAudioManager.AudioManagerEvents mAudioManagerEvents = new AppRTCAudioManager.AudioManagerEvents() {
        @Override
        public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
            PLog.d("onAudioDeviceChanged");
        }
    };

    public void onPause() {
//        if (peerConnectionClient != null) {
//            peerConnectionClient.stopVideoSource();
//        }

//        if (audioManager != null) {
//            audioManager.setMicrophoneMute(false);
//        }
    }

    public void onResume() {
//        if (peerConnectionClient != null) {
//            peerConnectionClient.startVideoSource();
//        }

//        if (audioManager != null) {
//            audioManager.setMicrophoneMute(true);
//        }
    }

    public void switchCamera() {
        if (peerConnectionClient != null) {
            peerConnectionClient.switchCamera();
        }
    }

    public void setAudioEnabled(boolean enabled) {
        if (peerConnectionClient != null) {
            peerConnectionClient.setAudioEnabled(enabled);
        }
    }

    public void release() {

        if (peerConnectionClient != null) {
            peerConnectionClient.close();
            peerConnectionClient = null;
        }

        if (audioManager != null) {
            audioManager.stop();
            audioManager = null;
        }
        if (mOptions != null && mOptions.rtcMsgClient != null) {
            mOptions.rtcMsgClient.removeRTCMsgArrivedListener(rtcMsgArrivedListener);
            mOptions.rtcMsgClient.release();
        }

        isStarted = false;
        mLiveRtcServiceListener = null;
    }

    public LiveRtcService(Context context, Options options) {
        mContext = context;
        mOptions = options;
        uid = options.Uid;
        callStartedTimeMs = System.currentTimeMillis();
        initRTCMsgArrivedListener();
    }


    public void initPeerConnection(Context appContext, EglBase eglBase) {
        peerConnectionParameters = PeerConnectionParameters.builder()
                .setAudioCall(mOptions.audioEnable)
                .setVideoCall(mOptions.videoEnable)
                .createPeerConnectionParameters();
        initPeerConnectionClient(appContext, eglBase);
        if (mOptions.audioEnable) {
            initRTCAudioManager();
        }

    }


    // Return the contents of an InputStream as a String.
    private static String drainStream(InputStream in) {
        Scanner s = new Scanner(in, "UTF-8").useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }

    private static final int TURN_HTTP_TIMEOUT_MS = 5000;

    private List<PeerConnection.IceServer> requestTurnServers(String url)
            throws IOException, JSONException {
        List<PeerConnection.IceServer> turnServers = new ArrayList<>();
        Log.d(TAG, "Request TURN from: " + url);
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setDoOutput(true);
        connection.setRequestProperty("REFERER", "https://appr.tc");
        connection.setConnectTimeout(TURN_HTTP_TIMEOUT_MS);
        connection.setReadTimeout(TURN_HTTP_TIMEOUT_MS);
        int responseCode = connection.getResponseCode();
        if (responseCode != 200) {
            throw new IOException("Non-200 response when requesting TURN server from " + url + " : "
                    + connection.getHeaderField(null));
        }
        InputStream responseStream = connection.getInputStream();
        String response = drainStream(responseStream);
        connection.disconnect();
        Log.d(TAG, "TURN response: " + response);

        JSONObject responseJSON = new JSONObject(response);
        JSONArray iceServers = responseJSON.getJSONArray("iceServers");
        for (int i = 0; i < iceServers.length(); ++i) {
            JSONObject server = iceServers.getJSONObject(i);
            JSONArray turnUrls = server.getJSONArray("urls");
            String username = server.has("username") ? server.getString("username") : "";
            String credential = server.has("credential") ? server.getString("credential") : "";
            for (int j = 0; j < turnUrls.length(); j++) {
                String turnUrl = turnUrls.getString(j);
                PeerConnection.IceServer turnServer =
                        PeerConnection.IceServer.builder(turnUrl)
                                .setUsername(username)
                                .setPassword(credential)
                                .createIceServer();
                turnServers.add(turnServer);
            }
        }
        return turnServers;
    }

    private void initPeerConnectionClient(Context appContext, EglBase eglBase) {
        // Create peer connection client.
        peerConnectionClient = new PeerConnectionClient(appContext, eglBase, peerConnectionParameters, this);
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
//        if (loopback) {
//            options.networkIgnoreMask = 0;
//        }
        peerConnectionClient.createPeerConnectionFactory(options);
    }

    private void initRTCAudioManager() {
        if (audioManager == null) {
            audioManager = AppRTCAudioManager.create(mContext);
        }
        audioManager.start(mAudioManagerEvents);
    }

    private void initRTCMsgArrivedListener() {
        if (mOptions.rtcMsgClient != null) {
            mOptions.rtcMsgClient.addRTCMsgArrivedListener(rtcMsgArrivedListener);
        }
    }


    private IRTCMsgClient.RTCMsgArrivedListener rtcMsgArrivedListener = new IRTCMsgClient.RTCMsgArrivedListener() {
        @Override
        public void onRTCMsgArrived(JSONObject param) {
            String cmd = "";
            try {
                cmd = param.getString("type");
            } catch (JSONException e) {
                e.printStackTrace();
                return;
            }
            if (cmd.equals("offer")) {

                ThreadUtils.post(new Runnable() {
                    @Override
                    public void run() {
                        if (mLiveRtcServiceListener != null) {
                            mLiveRtcServiceListener.recvOffer();
                        }
                    }
                });

                String sdp = null;
                try {
                    sdp = param.getString("sdp");
                    SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.OFFER, sdp);
                    onRemoteDescription(sessionDescription);
                    PLog.d("Creating ANSWER...");
                } catch (JSONException e) {
                    e.printStackTrace();
                }

            } else if (cmd.equals("busy")) {
                PLog.d("busy...");
                if (mLiveRtcServiceListener != null) {
                    mLiveRtcServiceListener.onBusy();
                }
            } else if (cmd.equals("offline")) {
                PLog.d("offline...");
                if (mLiveRtcServiceListener != null) {
                    mLiveRtcServiceListener.onOffLine();
                }
            } else if ("answer".equals(cmd)) {
                try {
                    String sdp = param.getString("sdp");
                    SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.ANSWER, sdp);
                    onRemoteDescription(sessionDescription);
                    PLog.d("recv ANSWER...");

                } catch (JSONException e) {
                    PLog.e("answer err, e: " + e.getMessage());
                }
            } else if ("candidate".equals(cmd)) {
                try {
                    IceCandidate localIceCandidate = new IceCandidate(param.getString("sdpMid"), param.getInt("sdpMLineIndex"), param.getString("candidate"));
                    onRemoteIceCandidate(localIceCandidate);
                    PLog.d("Add candidate...");
                } catch (JSONException e) {
                    PLog.e(TAG, "candidate err, e: " + e.getMessage());
                }
            } else if ("bye".equals(cmd)) {
                PLog.d("recv bye msg");
                if (mLiveRtcServiceListener != null) {
                    mLiveRtcServiceListener.onBye();
                }
            } else if ("ensure".equals(cmd)) {
                createAndsendOffer();
            }
        }
    };

    public void createPeerConnection(final VideoSink localProxyVideoSink, final List<VideoSink> remoteSinks,
                                     final VideoCapturer videoCapturer) {
        if (peerConnectionClient != null) {
            peerConnectionClient.createPeerConnection(localProxyVideoSink, remoteSinks, videoCapturer, mOptions.signalingParameters);
        } else {
            PLog.e("peerConnectionClient null");
        }
    }

    public void setOnLiveRtcServiceListener(LiveRtcServiceListener listener) {
        mLiveRtcServiceListener = listener;
    }

    public void createAndsendOffer() {
        peerConnectionClient.createOffer();
    }


    public void enableStatsEvents() {
        if (peerConnectionClient != null) {
            peerConnectionClient.enableStatsEvents(true, 1000);
        }
    }

    public boolean isInitiator() {
        return mOptions.initiator;
    }


    public void sendBye() {
        JSONObject params = new JSONObject();
        try {
            params.put("uid", mOptions.Uid);
            params.put("type", "bye");
        } catch (Exception e) {
            PLog.e("e: " + e.getMessage());
        }
        mOptions.rtcMsgClient.sendRTCMsg(params);
    }

    public void sendEnsureJoin() {
        JSONObject params = new JSONObject();
        try {
            params.put("type", "ensure");
        } catch (Exception e) {
            PLog.e("e: " + e.getMessage());
        }
//        SocketIOClient.getInstance().emit("bye", params.toString());
        isStarted = true;
        mOptions.rtcMsgClient.sendRTCMsg(params);
    }

    public void sendCallBackBye() {
        JSONObject params = new JSONObject();
        try {
            params.put("type", "bye");
        } catch (Exception e) {
            PLog.e("e: " + e.getMessage());
        }
        mOptions.rtcMsgClient.sendRTCMsg(params);
    }


    /*** bussisse  ***/
    public void onRemoteDescription(final SessionDescription sessionDescription) {
        PLog.d("onRemoteDescription, " + sessionDescription.type.toString());
        ThreadUtils.post(new Runnable() {
            @Override
            public void run() {
                if (peerConnectionClient == null) {
                    PLog.e("remote sdp not init");
                    return;
                }
                PLog.d("Received remote: " + sessionDescription.type.toString());
                peerConnectionClient.setRemoteDescription(sessionDescription);
                if (mOptions.initiator) {

                } else {
                    PLog.i("create answer");
                    peerConnectionClient.createAnswer();
                }
            }
        });
    }

    public void onRemoteIceCandidate(final IceCandidate iceCandidate) {
        ThreadUtils.post(new Runnable() {
            @Override
            public void run() {
                if (peerConnectionClient == null) {
                    PLog.e("recv ice not init");
                    return;
                }
                peerConnectionClient.addRemoteIceCandidate(iceCandidate);
            }
        });
    }

    /****  PeerConnectionEvents  ****/
    @Override
    public void onLocalDescription(final SessionDescription sdp) {
        long cost = System.currentTimeMillis() - callStartedTimeMs;
        PLog.d("onLocalDescription, delta: " + cost + ", sdpType: " + sdp.type);
        ThreadUtils.post(new Runnable() {
            @Override
            public void run() {
                if (mOptions.signalingParameters.initiator) {
                    JSONObject offer = new JSONObject();
                    jsonPut(offer, "sdp", sdp.description);
                    jsonPut(offer, "type", "offer");
                    if (mOptions.rtcMsgClient != null) {
                        mOptions.rtcMsgClient.sendRTCMsg(offer);
                    }
                } else {
                    JSONObject msg = new JSONObject();
                    jsonPut(msg, "sdp", sdp.description);
                    jsonPut(msg, "type", "answer");
                    mOptions.rtcMsgClient.sendRTCMsg(msg);
                    PLog.d("onLocalDescription, send answer: " + msg.toString());
                }
                if (peerConnectionParameters.videoMaxBitrate > 0) {
                    PLog.i("Set video maximum bitrate: " + peerConnectionParameters.videoMaxBitrate);
                    peerConnectionClient.setVideoMaxBitrate(peerConnectionParameters.videoMaxBitrate);
                }
            }
        });
    }

    @Override
    public void onIceCandidate(final IceCandidate candidate) {
        long cost = System.currentTimeMillis() - callStartedTimeMs;
        PLog.d("onIceCandidate, delta: " + cost + ", candidate: " + candidate.toString());
        ThreadUtils.post(new Runnable() {
            @Override
            public void run() {
                try {
                    JSONObject jsoncandidate = new JSONObject();
                    jsoncandidate.put("type", "candidate");
                    jsoncandidate.put("sdpMLineIndex", candidate.sdpMLineIndex);
                    jsoncandidate.put("sdpMid", candidate.sdpMid);
                    jsoncandidate.put("candidate", candidate.sdp);
                    if (mOptions.rtcMsgClient != null) {
                        if (isInitiator()) {
                            mOptions.rtcMsgClient.sendRTCMsg(jsoncandidate);
                        } else {
                            mOptions.rtcMsgClient.sendRTCMsg(jsoncandidate);
                        }
                    }
                } catch (Exception e) {
                    PLog.d("onIceCandidate, err: " + e.getMessage());
                }
            }
        });
    }

    @Override
    public void onIceCandidatesRemoved(IceCandidate[] candidates) {
        PLog.d("onIceCandidatesRemoved");
    }

    @Override
    public void onIceConnected() {
        callEndTimeMs = System.currentTimeMillis();
        PLog.d("onIceConnected");
        if (mLiveRtcServiceListener != null) {
            mLiveRtcServiceListener.onIceConnected();
        }
//        // 去掉超时连接
////        removeCallbacks(mJoinTimeoutRunnable);
//        ThreadUtils.post(new Runnable() {
//            @Override
//            public void run() {
//                LogUtil.i(TAG, "ice connected");
////                callConnected();
//            }
//        });

        RtcAnalysisUtil.getInstance().uploadAnalysis(mContext, uid, did, callStartedTimeMs, callEndTimeMs, true, "");
    }

    @Override
    public void onIceDisconnected() {
        PLog.d("onIceDisconnected");
        if (mLiveRtcServiceListener != null) {
            mLiveRtcServiceListener.onIceDisconnected();
        }
    }

    @Override
    public void onPeerConnectionClosed() {
        if (mLiveRtcServiceListener != null) {
            mLiveRtcServiceListener.onPeerConnectionClosed();
        }
        PLog.d("onPeerConnectionClosed");
    }

    @Override
    public void onPeerConnectionStatsReady(StatsReport[] reports) {
        if (reports != null) {

            WebRtcStats stats = new WebRtcStats();

            for (StatsReport report : reports) {
                if (report == null) {
                    continue;
                }
                //打印
                if (peerConnectionParameters.enableStats) {
                    PLog.d("---------" + report.type + " Stats Report---------");
                    if (report.values != null) {
                        for (StatsReport.Value value : report.values) {
                            PLog.d(value.name + "=" + value.value);
                        }
                    }
                }

                //获取带宽信息
                if ("VideoBwe".equals(report.type)) {
                    if (report.values != null) {
                        for (StatsReport.Value value : report.values) {
                            if (value == null) {
                                continue;
                            }
                            if ("googActualEncBitrate".equals(value.name)) {
                                stats.setActualEncBitrate(value.value);
                            }

                            if ("googTransmitBitrate".equals(value.name)) {
                                stats.setTransmitBitrate(value.value);
                            }

                            if ("googTargetEncBitrate".equals(value.name)) {
                                stats.setTargetEncBitrate(value.value);
                            }

                            if ("googAvailableSendBandwidth".equals(value.name)) {
                                stats.setAvailableSendBandwidth(value.value);
                            }
                        }
                    }
                }

                // 获取激活的CandidatePair的googRemoteCandidateType值
                if ("googCandidatePair".equals(report.type)) {
                    if (report.values != null) {
                        StatsReport activeGoogCandidatePair = null;
                        for (StatsReport.Value value : report.values) {
                            if (value == null) {
                                continue;
                            }
                            if ("googActiveConnection".equals(value.name) && "true".equals(value.value)) {
                                activeGoogCandidatePair = report;
                                break;
                            }
                        }

                        if (activeGoogCandidatePair != null) {
                            for (StatsReport.Value value : activeGoogCandidatePair.values) {
                                if (value == null) {
                                    continue;
                                }
                                if ("googRemoteCandidateType".equals(value.name)) {
                                    stats.setCandidateType(value.value);
                                }
                            }
                        }
                    }
                }

                if ("ssrc".equals(report.type)) {
                    if (report.values != null) {
                        StatsReport ssrcVideoReceived = null;
                        boolean switch1 = false, switch2 = false;
                        for (StatsReport.Value value : report.values) {
                            if (value == null) {
                                continue;
                            }
                            if ("mediaType".equals(value.name) && "video".equals(value.value)) {
                                switch1 = true;
                                continue;
                            }
                            if ("bytesReceived".equals(value.name)) {
                                switch2 = true;
                                continue;
                            }
                            if (switch1 && switch2) {
                                ssrcVideoReceived = report;
                            }
                        }

                        if (ssrcVideoReceived != null) {
                            for (StatsReport.Value value : ssrcVideoReceived.values) {
                                if (value == null) {
                                    continue;
                                }
                                if ("googCurrentDelayMs".equals(value.name)) {
                                    stats.setCurrentDelayMs(value.value);
                                }
                            }
                        }

                    }
                }

                if ("ssrc".equals(report.type)) {
                    if (report.values != null) {
                        StatsReport ssrcVideoSend = null;
                        boolean switch1 = false, switch2 = false;
                        for (StatsReport.Value value : report.values) {
                            if (value == null) {
                                continue;
                            }
                            if ("mediaType".equals(value.name) && "video".equals(value.value)) {
                                switch1 = true;
                                continue;
                            }
                            if ("bytesSent".equals(value.name)) {
                                switch2 = true;
                                continue;
                            }
                            if (switch1 && switch2) {
                                ssrcVideoSend = report;
                            }
                        }

                        if (ssrcVideoSend != null) {
                            for (StatsReport.Value value : ssrcVideoSend.values) {
                                if (value == null) {
                                    continue;
                                }
                                if ("googFrameWidthSent".equals(value.name)) {
                                    stats.setSendWidth(value.value);
                                }
                                if ("googFrameHeightSent".equals(value.name)) {
                                    stats.setSendHeight(value.value);
                                }
                                if ("googFrameRateSent".equals(value.name)) {
                                    stats.setSendFrameSend(value.value);
                                }
                                if ("googBandwidthLimitedResolution".equals(value.name)) {
                                    stats.setBandwidthLimitedResolution(value.value);
                                }
                            }
                        }

                    }
                }
            }

            if (mLiveRtcServiceListener != null) {
                mLiveRtcServiceListener.onStatsReady(stats);
            }
        }

    }


    @Override
    public void onPeerConnectionError(String description, int errorType) {
        callEndTimeMs = System.currentTimeMillis();
        RtcAnalysisUtil.getInstance().uploadAnalysis(mContext, uid, did, callStartedTimeMs, callEndTimeMs, false, "");

        PLog.d("onPeerConnectionError");
        if (mLiveRtcServiceListener != null) {
            mLiveRtcServiceListener.onPeerConnectionError(description, errorType);
        }
    }

    @Override
    public void onDataChannel(DataChannel dc) {
        if (mLiveRtcServiceListener != null) {
            mLiveRtcServiceListener.onDataChannel(dc);
        }
    }


    private void jsonPut(JSONObject paramJSONObject, String paramString, Object paramObject) {
        try {
            paramJSONObject.put(paramString, paramObject);
            return;
        } catch (JSONException localJSONException) {
            throw new RuntimeException(localJSONException);
        }
    }

    public boolean sendDataByDataChannel(String msg, boolean binary) {
        boolean bool = false;
        if (peerConnectionClient != null) {
            peerConnectionClient.sendDataByDataChannel(msg, binary);
        }
        return bool;
    }

    public boolean sendDataByDataChannel(byte[] data) {
        boolean bool = false;
        if (peerConnectionClient != null) {
            bool = peerConnectionClient.sendDataByDataChannel(data);
        }
        return bool;
    }

    public interface LiveRtcServiceListener {
        void onIceConnected();

        void onIceDisconnected();

        void onPeerConnectionClosed();

        void onDataChannel(DataChannel dc);

        void recvOffer();

        void onBusy();

        void onOffLine();

        void onPeerConnectionError(String errorMessage, int errorType);

        void onBye();

        void onStatsReady(WebRtcStats stats);

    }


    public boolean isRemoteVideoEnable() {
        if (peerConnectionClient != null) {
            return peerConnectionClient.isRemoteVideoEnabled();
        }
        return false;
    }

    public void setRemoteVideoEnabled(boolean enabled) {
        if (peerConnectionClient != null) {
            peerConnectionClient.setRemoteVideoEnabled(enabled);
        }
    }

    public void setLocalVideoEnabled(boolean enabled) {
        if (peerConnectionClient != null) {
            peerConnectionClient.setLocalVideoEnabled(enabled);
        }
    }

    public PeerConnection.IceConnectionState iceConnectionState() {
        if (peerConnectionClient != null) {
            return peerConnectionClient.iceConnectionState();
        }
        return null;

    }
}

